home *** CD-ROM | disk | FTP | other *** search
/ Freaks Macintosh Archive / Freaks Macintosh Archive.bin / Freaks Macintosh Archives / Textfiles / zines / DNA / DNAV1I9.sit / DNAV1I9 / DNA109.012 < prev    next >
Text File  |  1994-08-24  |  22KB  |  499 lines

  1.                  _         _/ \_             _/ \_          _
  2.                _/ \_      /  _  \__        _/  _  \_      _/ \_
  3.              _/  _  \_______/ \  | \_      |  / \ _______/  _  \_
  4.             /  _/ \         \_ | |\_ \_    | |  _/         / \_  \
  5.             \ /    | |----\_  \_ |  \_ \_  | |_/  _/----| |    \ /
  6.                    | |      \_  \|    \_ \_| /   /      | |
  7.                    | |     _/   ||        \||    \_     | |
  8.                    | |   _/   _/ |         | \_    \_   | |
  9.             / \_   | |__/   _/ | |         | | \_    \__| |   _/ \
  10.             \_  \_/  ______/\_/  |         |  \_/\_______  \_/  _/
  11.               \_   _/     \_   _/           \_   _/      \_   _/
  12.                 \_/         \_/               \_/          \_/
  13.                          
  14.  
  15.                          VGA Programming
  16.  
  17.      Although this subject is not one that normally accompanies a
  18. typical P/H magazine, many people have asked questions about both
  19. VGA and SVGA graphics programming, and I've decided to write up the
  20. article as a response to all of them, instead of replying to each
  21. individually. In this article, I'm not going to address SVGA
  22. programming, since it's such a huge subject, and one that I need to
  23. work on more. While this is far from a step by step tutorial in how
  24. to infiltrate a specific system, I like to think that broad
  25. knowledge of all aspects of programming are an integral part of
  26. being a successful hacker. In that light, I hope you read and enjoy
  27. what I have to present here. I have drawn this information from a
  28. tremendous variety of sources. Many are books, and many are people
  29. whose names are forgotten, but whose contributions are not.
  30.  
  31.                      What's Required of You
  32.  
  33.      I assume you have some programming knowledge. If you don't,
  34. this article will be virtually useless to you. All my sample code
  35. is in either C or assembler. Most of it is pretty simple stuff, so
  36. if you know either one even to some small amount, you'll be able to
  37. handle this pretty easily. This is geared towards those who
  38. understand programming well, and want to learn more about how to
  39. take full control over thier systems. Understanding video cards
  40. completely is a good start, and something that will take quite a
  41. bit of time to get down. If you aren't a programmer, don't bother
  42. reading on, you're just going to get bored to death learning about
  43. VGA internals and the like. If you are, read on! You'll like what
  44. you can do once you're done.
  45.  
  46.                         VGA architecture
  47.  
  48.      Before I can go into detail on how to program a VGA card to do
  49. what you want, I need to describe some of it's architecture. First
  50. of all, standard VGA graphic's highest useful mode is 320 x 200 in
  51. 256 colors. While it is a bit crude looking, it is very useful for
  52. plenty of applications. If you want to do better, you will need to
  53. start dealing with SVGA graphics, which are far less standard than
  54. VGA. I will go into SVGA graphics at another time. The subject is
  55. just too damn big to put into this introductory text. Next, video
  56. displays are what are called memory mapped devices. What does this
  57. mean? Well, everyone knows that there is memory on your VGA card.
  58. Typically, your standard SVGA card has 1 megabyte of VRAM on the
  59. board. How do you access this memory on the card? The answer is
  60. that an entire segment of memory(64k bytes) is reserved in your
  61. first megabyte of system memory. Anything you write to this segment
  62. of memory gets copied to the VGA card's memory directly. The base
  63. memory is 'mapped' into the VRAM on the VGA card. Whatever is put
  64. into the VRAM is what is displayed on the screen. It's simply a
  65. matter of writing the right information in the right order.
  66.  
  67.                     Accessing the Memory Map
  68.  
  69.                In standard VGA modes, you can't access more than the first
  70. 64k of RAM(out of the typical one meg) on the card. This is just a
  71. limitation put in place by the current standards. DOS actually sets
  72. aside two whole segments of memory for video display. However, one
  73. is dedicated to text displays, and the other is for true graphics.
  74. The size of the memory map for 320 by 200 is exactly 64000(320 *
  75. 200) bytes. The segment for graphics displays is located at 0A000h.
  76. Writing to this segment will produce changes on your screen
  77. assuming you are currently in graphics mode. 
  78.  
  79.                   Understanding the Memory Map
  80.  
  81.      Drawing single pixels on the screen is an extremely simple
  82. task. Doing so requires you to write a single byte into the graphic
  83. memory map at 0A000h. However, it's probably important to you
  84. *where* on the screen that pixel is going to show up, so it's needs
  85. to be discussed how the memory map relates to the pixels on the
  86. screen. To do this, we need to consider the coordinate system for
  87. IBM monitors. First of all, the starting corner of the screen is
  88. the upper left. Think of that corner of the screen as (0,0). X
  89. increases as you move further to the right. Y increases as you move
  90. down. Obviously, in this graphics mode, you can have X values from
  91. 0 to 319, for a total of 320 pixels across the screen. Similarly,
  92. you can have pixels from 0 to 199 for Y, totalling 200 pixels
  93. vertically. As I've mentioned, the full address of the beginning of
  94. the VGA screen is at 0xA000:0x0000. The '0x' before the numbers
  95. denote the fact that they are in hexadecimal notation(base 16). To
  96. move on to the next pixel (1,0), you would read from the offset
  97. 0x0001. Pixel (2,0) would be 0x0002, and so on. This is very easy,
  98. until you hit the end of the line. The last pixel at the top of the
  99. screen (319,0) is at offset 0x013F(319 decimal). The next line
  100. starts at the very next byte in memory. So, the offset for (1,0) is
  101. 0x140(320 decimal). (1,1) is 0x0141. (1,2) is 0x0142 and so on.
  102. Understanding how the memory is mapped on the screen is important,
  103. since it's this map that you use to make useful images. If you
  104. don't understand this, you've got to go back and read it again
  105. until you do.
  106.  
  107. Imagine the asterisks below are the pixels of the screen, starting
  108. at the upper left. The numbers to the left and right of the
  109. 'pixels' are the offsets of the first and last pixels on that line,
  110. respectively.
  111.                            VGA SCREEN:
  112. 0   |****************************************************| 319
  113. 320 |****************************************************| 639
  114. 640 |****************************************************| 959
  115. 960 |****************************************************|1279
  116. 1280|****************************************************|1599
  117.  
  118.                        Formula for Pixels
  119.  
  120.      Now that you understand the memory mapping, how do you find
  121. the offset for a given pixel on the screen? This is the toughest
  122. part of writing to VGA screens. The algorithm itself is extremely
  123. easy. The offset of an arbitrary pixel found with this algorithm:
  124. Offset = (Y * 320) + X
  125. This formula makes sense if you think about how the screen is laid
  126. out. If you want to move down to line number Y, you have to add 320
  127. to the offset to reach that point for every line you move down. If
  128. you don't understand what I mean, just accept that the formula is
  129. correct, and figure it out for yourself later. Now that you know
  130. the formula, you can write a generic function or macro that does
  131. this in either C, assembly, or some other language. Personally,
  132. considering how simple an algorithm this is, I'd go ahead and code
  133. it in assembly. Besides, it's handy to stay away from the
  134. multiplication functions that high level languages use. Here is
  135. some very quick code to determine offsets given an X and a Y value:
  136.  
  137. MOV         AX,Y
  138. MOV         BX,X
  139. MOV         CL,6
  140. SHL         AX,CL   ; Fast multiply of Y times 64
  141. ADD         BX,AX   ; Add Y to X
  142. ADD         AX,AX   ; Multiply Y times 2 again
  143. ADD         AX,AX   ; Multiply Y times 2 again
  144. ADD         BX,AX   ; Add total result into BX
  145.  
  146. If you have no way of using assembly, by all means use similar code
  147. in whatever language you wish. For example, a function in C would
  148. look like this:
  149.  
  150.  
  151. unsigned char far *screen;
  152. screen = MK_FP(0xA000,(y * 320) + x);
  153.  
  154. Here's a tiny C program to calculate offsets for you, in both
  155. Decimal and Hex:
  156.  
  157. #include <stdio.h>
  158. #include <dos.h>
  159.  
  160. void main(void)
  161. {
  162. int x,y;
  163. unsigned int offset;
  164. printf("Input X and Y coordinates for VGA 13h mode offset:\n");
  165. scanf("%d",&x);
  166. scanf("%d",&y);
  167. offset = (y * 320) + x;
  168. printf("%d,   0x%x\n",offset,offset);
  169. }
  170.  
  171. Most of my example code in this article will be in C, since with
  172. this graphic mode, it's not very critical that it be optimized to
  173. death. When dealing with SVGA graphics, writing efficient code is
  174. nearly impossible in C. But we're not there yet, and we should be
  175. thankful for that.
  176.  
  177.                                   Accessing Memory
  178.      Now that we have our memory address stored into a far pointer,
  179. how do we use that information? In fact, it's extremely easy. You
  180. will only need to deal with offsets once the '*screen' pointer has
  181. been setup. You will never need to change the value of *screen,
  182. since you can now access screen as an array of characters. To write
  183. a pixel of arbitrary value '87' to the location (0,0) on the
  184. monitor:
  185.  
  186. unsigned char far *screen;
  187. screen = MK_FP(0xA000,0);
  188. screen[0] = 87;
  189.  
  190. That's all there is to it! Once you're in graphics mode, you simply
  191. choose the offset of the pixel you want to write, and put that
  192. offset into the screen array to be dereferenced. We can use the
  193. above formula we learned to make a general function to write a
  194. pixel to any spot on the screen:
  195.  
  196. int plotpoint(int x, int y, char color)
  197. {
  198. if( (x > 319) || (x < 0) || (y > 199) || (y < 0) )
  199.   return 0;
  200. screen[(y * 320) + x] = color;
  201. return 1;
  202. }
  203.  
  204.      This code does assume that the '*screen' pointer is a global
  205. variable. I'm sure we all know that using global variables is 'bad'
  206. by most standards. Well, damn it, the screen object is something
  207. that lots of functions need access to, and there's only one screen
  208. anyway, right? Sending another 32 bit pointer on the stack every
  209. time you want to use a subroutine that accesses the screen is
  210. wasting time and space. This routine returns a 1 if it's
  211. successful, and 0 if the X or Y coordinates were out of bounds.
  212.      Reading a pixel is just as easy as setting it. You use the
  213. same global *screen pointer to access it, but read from the memory
  214. location rather than writing to it. Here it is:
  215.  
  216. int readpoint(int x, int y, char *color)
  217. {
  218. if( (x > 319) || (x < 0) || (y > 199) || (y < 0) )
  219.   return 0;
  220. color = screen[(y * 320) + x];
  221. return 1;
  222. }
  223.  
  224.      This subroutine returns the same values as the one above, and
  225. it also returns the value of the color. Remember you have to send
  226. the function the pointer to your color variable when calling this
  227. routine, thusly:
  228.  
  229. unsigned char pixel;
  230. readpoint(45,56,&pixel);
  231.                All this is pretty straight forward coding. C is very flexible
  232. when it comes to arranging memory. The most difficult part is
  233. managing all the memory tasks by yourself. Don't be surprised when
  234. you get lots of null pointer assignments and the like. Working with
  235. memory and pointers is extremely taxing and tedious. Getting it
  236. right is tough, but rewarding in the end.
  237.  
  238.      As it turns out, dereferencing a far pointer every time you
  239. want to write or write a pixel on the screen isn't terribly
  240. efficient. If you are going to read or write in a row, it is a much
  241. better idea to optimize that memory access at the same time. You
  242. can do it with C, or write yourself a very simple, and highly
  243. optimized assembly routine to do the same thing. Let's say you have
  244. a buffer(array of chars) which you want to write directly to the
  245. screen. How can you move that to the position you want on the
  246. screen directly without dereferencing far pointers a lot? Here it
  247. is in assembly:
  248.  
  249. g_memwrite proc uses ES SI DI, inbuf:WORD, start:WORD, length:WORD
  250.      MOV         CX,length    ; How many bytes to write
  251.      MOV         AX,0A000h    ; Video Memory segment
  252.      MOV         SI,inbuf     ; offset of byte array to be written
  253.      MOV         ES,AX           ; Set ES to video memory segment
  254.      MOV         DI,start     ; Where on screen to start(offset)
  255.      REP         MOVSB           ; Write CX bytes to screen
  256.      RET
  257. g_memwrite endp
  258.  
  259.  
  260.      Here are some useful functions in assembly for working with
  261. mode 13h VGA. For setting the VGA screen to graphics mode:
  262.  
  263. void set_vgamode(void);
  264.  
  265. set_vgamode PROC
  266.      MOV  AX,13h
  267.      INT  10h
  268.      RET
  269. set_vgamode  ENDP
  270.  
  271. For setting back to 80x25 text mode:
  272.  
  273. void set_textmode(void);
  274.  
  275. set_textmode PROC
  276.      MOV  AX,3
  277.      INT  10h
  278.      RET
  279. set_textmode ENDP
  280.  
  281.  
  282. For clearing the whole screen to a given color:
  283.  
  284. void clearscrn(char color);
  285. clearscrn PROC USES ES DI, color:byte
  286.      MOV  AX,0A000h
  287.      MOV  ES,AX               ;Point ES:DI to
  288.      XOR  DI,DI               ; the screen map
  289.      MOV  CX,64000         ;Set repeat to 64000
  290.      MOV  AL, color       ;Color to be written to all pixels
  291.      REP  STOSB               ;Write all pixels at once
  292.      RET
  293. clearscrn ENDP
  294.  
  295. For writing a pixel to the screen at (X,Y) with a given color.
  296. Requires a 286 to execute this code, since it uses immediates for
  297. the shifts:
  298.  
  299. void plotpoint(int x, int y, char color);
  300.  
  301. plotpoint proc USES ES, X:word, Y:word, color:byte
  302.      MOV  AX,0A000h
  303.      MOV  ES,AX
  304.      MOV  AX,Y
  305.      MOV  BX,X
  306.      SHL  AX,6
  307.      ADD  BX,AX
  308.      SHL  AX,2
  309.      ADD  BX,AX
  310.      MOV  AL, byte ptr color
  311.      MOV  ES:[BX], AL
  312.      RET
  313. plotpoint ENDP
  314.  
  315. Now that we've gotten into how to write directly to the video
  316. screen, let's look at another important subject in video
  317. programming. The VGA palette may be the most difficult aspect of
  318. VGA programming to grasp.
  319.  
  320.                            VGA Palette
  321.      Now that we understand how to write certain values into video
  322. memory, it's time to discuss how those values are translated into
  323. color. A palette is how you describe what colors are going to be
  324. shown on the screen for a given value. Let's say you write the
  325. value '68' to some pixel on your screen. How does your VGA card
  326. decide what color is associated with that number 68? How do you set
  327. your card to display a given color? The VGA palette for each of the
  328. 256 colors is a 3 byte series. The first byte is the value for Red,
  329. the second for Green, and the third for Blue. Each of those bytes
  330. can only use the bottom 6 bits, meaning that their values can be
  331. between 0 and 63 only. Being at a value of 0 means that the color
  332. is completely turned off. The color black is achieved by setting
  333. all three bytes to 0. Setting a color to 63 turns it full on.
  334. Setting all three bytes to 63 will be the color white. Everything
  335. else inbetween can be achieved with different combinations. There
  336. are a total of 262,144 colors that can be displayed on a VGA card,
  337. 256 of which can be displayed at once. Since there are 256 colors
  338. in the VGA palette, the palette in memory takes up 768 bytes(256 *
  339. 3). If you are going to manipulate the palette, you're going to
  340. want to keep it in memory, since reading from the card is slow and
  341. difficult.
  342.  
  343.                     Manipulating the Palette
  344.      Writing the values you want to the VGA card is actually quite
  345. simple. While there are other considerations when you're doing
  346. this, all you have to do is write three bytes to the correct IO
  347. port. First, to write to the palette for color number 68 for
  348. example, you have to write the number 68 to port 0x3C8. After that,
  349. you have to write the three byte values in order to port number
  350. 0x3C9. If you don't understand this, look at the pseudocode
  351. example:
  352.  
  353. write 68 to port 0x3C8
  354. write Red byte to port 0x3C9
  355. write Green byte to port 0x3C9
  356. write Blue byte to port 0x3C9
  357.  
  358. It's quite simple, as you can see. The only difficult part of this
  359. is setting the values of the palette to values you want. There is
  360. one level of complication that needs to be added to this simple
  361. model though. On some systems, this will cause lots of static, or
  362. snow on the screen whenever a palette register is set. This is
  363. caused for some esoteric reason on crappy VGA cards. The way around
  364. this is to only write to the screen while the monitor is retracing.
  365. If you don't know how a monitor paints the screen, it moves an
  366. electron beam along each of the pixel lines, and when it gets to
  367. the bottom, it has to move the beam back up to the upper left of
  368. the screen to start drawing again. While it's moving back up to the
  369. top of the screen, obviously nothing is being written to the
  370. monitor. This is good for us, since no static shows up at this time
  371. either. All we have to do is make sure we're in a retrace state,
  372. and then set our palette registers. Checking retrace requires that
  373. you read port 0x3DA. Reading a port means that you get a byte of
  374. data in return. Only the third bit of data is useful to us right
  375. now. If that bit is a one, then the screen is in a retrace
  376. currently, and anything written to it will have little or no snow.
  377. We need to make sure that we don't accidentally come by the retrace
  378. at the very last second, and write a whole bunch of data to the VGA
  379. card even after it's done with the retrace. To stop that, we will
  380. have two loops that wait for the retrace. That being the case, lets
  381. examine the new p-code.
  382.  
  383. non_retrace:
  384. read port 0x3DA
  385. if bit 3 = 1 goto non_retrace
  386.  
  387. retrace:
  388. read port 0x3DA
  389. if bit 3 = 0 goto retrace
  390.  
  391. write 68 to port 0x3C8
  392. write Red byte to port 0x3C9
  393. write Green byte to port 0x3C9
  394. write Blue byte to port 0x3C9
  395.  
  396. Typically, about 128 of the 256 color palette can be written in a
  397. single retrace, so loop the last statement for 128 times:
  398.  
  399. non_retrace:
  400. read port 0x3DA
  401. if bit 3 = 1 goto non_retrace
  402.  
  403. retrace:
  404. read port 0x3DA
  405. if bit 3 = 0 goto retrace
  406.  
  407. for x=0 to 127
  408.   {
  409.   write x to port 0x3C8
  410.   write Red byte for color x to port 0x3C9
  411.   write Green byte for color x to port 0x3C9
  412.   write Blue byte for color x to port 0x3C9
  413.   }
  414.  
  415. Rewriting this in another language shouldn't take too long. Here is
  416. the code to do just this in C:
  417.  
  418. set_vgapalette(char *p)  //p is the 768 byte palette array
  419. {
  420. unsigned int i,j,end;
  421. for(i=1; i<=2; i++)       //Do twice
  422.   {
  423.   end = 128 * i;
  424.   while( 1 == inp(0x3DA) & 0x08 ); //Loop while bit 3 = 1(retrace)
  425.   while( 0 == inp(0x3DA) & 0x08 ); //Loop while bit 3 = 0(not)
  426.   for(j=128 * (i - 1); j<end; j++) //Write first or second 128
  427.     {                                                                                                         //  colors to palette registers
  428.     outp(0x3C8,j);         //Color to be written next
  429.     outp(0x3C9,p++);     //Red byte of palette color
  430.     outp(0x3C9,p++);     //Green byte..
  431.     outp(0x3C9,p++);     //Blue byte..
  432.     }
  433.   }
  434. }
  435.  
  436. This is a pretty quick little routine for setting the palette. Of
  437. course, this sets the entire palette every time you call it. If you
  438. only are changing a small part of the palette, it'll be faster to
  439. write a function that sets only that portion you changed. Another
  440. option is to rewrite this in optimized assembly code. Personally,
  441. I think all your VGA primitives should be written in assembly for
  442. the sake of speed and size. If you don't know assembly though,
  443. you're pretty much out of luck. All I can do is give you some
  444. standard routines to use for yourself, and recommend you try
  445. learning it. It's a big help to you when writing in every language.
  446. For instance, many C or Pascal compilers have an option of
  447. compiling your C code to assembly. Being able to see what the
  448. compiler does with your code can help you optimize even straight C
  449. routines a lot. For instance, in the code above, I put made the
  450. variable 'end' because I looked at how C assembles the FOR loop,
  451. and realized that it calculates the second part of the FOR
  452. statement every time it loops. Instead of multiplying I*128 every
  453. time the loop comes by, it just compares it with the memory
  454. location 'end'. It's much faster, and makes the loop quicker, so
  455. there's no snow on the screen. Enough crap on the wrong subject. 
  456.  
  457.      Let's get back to palette functions. Since you can change the
  458. palette while you've already got something displayed on the screen,
  459. interesting effects can be produced. Let's say you want to fade
  460. your screen at the end of a sequence to black. The value black is
  461. achieved by setting all three palette colors to zero. So, if we
  462. want to fade the entire palette to black over time, we just have to
  463. subtract one from each of the 768 byte colors from the palette in
  464. memory. Look at the code, not at my writing:
  465.  
  466. for(i=0; i<768; i++)
  467.   {
  468.   if palette[i] > 0 then palette[i] = palette[i] - 1;
  469.   }
  470. set_vgapalette(palette);
  471.  
  472. This code does just what I mentioned. It subtracts one from each
  473. byte color, and once it's done all 768 of them, it displays the new
  474. palette using the set_vgapalette function. Many other interesting
  475. functions can be achieved using palette changing routines such as
  476. this. You could fade to white, slowly swap from one palette to
  477. another, rotate palettes to create a moving effect. For instance,
  478. you could make an fiery explosion seem to move by changing the
  479. palette from white to yellow to red slowly. The particular
  480. application is up to you; use your imagination.
  481.  
  482.      That's about all there is too it. There are lots of other
  483. graphics modes you could use, and lots of other things that could
  484. be mentioned. However, only so much can be taught to you. Much of
  485. the understanding necessary for writing good graphics code must
  486. simply be learned by trial and error coming from hands on
  487. programming. While I don't fool myself into believing many people
  488. will benefit from this introduction, I hope someone enjoyed it. I
  489. have spent many, many hours learning VGA and SVGA programming, and
  490. I hope this little beginning will be enough for at least a few
  491. individuals to start off on their own. If you read this and have
  492. questions, or would like to see another article written on a more
  493. specific subject, please do mail me on Digital Decay, or some board
  494. that picks up DNAnet. I'll probably do an article on SVGA cards at
  495. a later time, but I need more time to work on that. Until such
  496. time, pleasant programming and happy hacking.
  497.  
  498. Zephyr [Cosys - Digital Decay] 8/94
  499.